﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace 数字孪生Demo.粒子特效
{
    /// <summary>
    /// Particle.xaml 的交互逻辑
    /// </summary>
    public partial class ParticleWindow : Window
    {

        class Particle
        {
            public double x, y, z;
            public double vy;
            public double radius;
            public double colorVal;
            public double dist;
        }

        // 配置 —— 与原 JS 接近
        int initParticles = 700;
        double initV = 0.01;
        double distributionRadius = 800;
        double vortexHeight = 25;

        // 相机变量（与原 JS 对应）
        double camX = 0, camY = 0, camZ = -14;
        double yaw = 0, pitch = 0;
        int frameNo = 0;

        readonly Random rnd = new Random();
        List<Particle> points = new List<Particle>();

        // 历史帧缓冲，用于拖影（保存最近 n 帧的点集合）
        readonly int trailFrames = 6;
        Queue<Point3DCollection[]> trailQueue = new Queue<Point3DCollection[]>();

        // bucket 数量（颜色桶）
        const int BUCKETS = 6;

        DispatcherTimer timer;

        public ParticleWindow()
        {
            InitializeComponent();

            InitFloor();
            InitParticles();

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(16); // ~60fps
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        void InitFloor()
        {
            // 建立 floor 点集合（两个层分别代表上面和下面，以便模拟原 JS 不同配色）
            var pts1 = new Point3DCollection();
            var pts2 = new Point3DCollection();
            for (int i = -25; i <= 25; i++)
            {
                for (int j = -25; j <= 25; j++)
                {
                    double x = i * 2.0;
                    double z = j * 2.0;
                    double d = Math.Sqrt(x * x + z * z);
                    pts1.Add(new Point3D(x, 26.5 - d * d / 85.0, z)); // 上层
                    pts2.Add(new Point3D(x, -26.5 + d * d / 85.0, z)); // 下层
                }
            }
            FloorLayer1.Points = pts1;
            FloorLayer2.Points = pts2;
            // Layer 颜色在 XAML 设置
        }

        void InitParticles()
        {
            points.Clear();
            for (int i = 0; i < initParticles; i++)
            {
                SpawnParticle();
            }

            // 初始化 trailQueue 的空集合（BUCKETS 个集合每帧）
            for (int t = 0; t < trailFrames; t++)
            {
                var arr = new Point3DCollection[BUCKETS];
                for (int b = 0; b < BUCKETS; b++) arr[b] = new Point3DCollection();
                trailQueue.Enqueue(arr);
            }
            // 最开始把当前点分桶一次
            CommitTrail();
        }

        void SpawnParticle()
        {
            var pt = new Particle();
            double p = Math.PI * 2 * rnd.NextDouble();
            double ls = Math.Sqrt(rnd.NextDouble() * distributionRadius);
            pt.x = Math.Sin(p) * ls;
            pt.y = -vortexHeight / 2.0;
            pt.vy = initV / 20.0 + rnd.NextDouble() * initV;
            pt.z = Math.Cos(p) * ls;
            pt.radius = 200 + 800 * rnd.NextDouble();
            pt.colorVal = pt.radius / 1000.0 + frameNo / 250.0;
            points.Add(pt);
        }

        void Timer_Tick(object sender, EventArgs e)
        {
            frameNo++;
            Process();
            CommitTrail();
            RenderBuckets();
        }

        void Process()
        {
            // 保持粒子数
            while (points.Count < initParticles) SpawnParticle();

            double p = Math.Atan2(camX, camZ);
            double d = Math.Sqrt(camX * camX + camZ * camZ);
            d -= Math.Sin(frameNo / 80.0) / 25.0;
            double t = Math.Cos(frameNo / 300.0) / 165.0;
            camX = Math.Sin(p + t) * d;
            camZ = Math.Cos(p + t) * d;
            camY = -Math.Sin(frameNo / 220.0) * 15.0;
            yaw = Math.PI + p + t;
            pitch = Elevation(camX, camZ, camY) - Math.PI / 2.0;

            // 更新每个粒子
            for (int i = points.Count - 1; i >= 0; --i)
            {
                var pt = points[i];
                double x = pt.x;
                double y = pt.y;
                double z = pt.z;
                double dd = Math.Sqrt(x * x + z * z) / 1.0075;
                double tt = .1 / (1 + dd * dd / 5.0);
                double ang = Math.Atan2(x, z) + tt;
                pt.x = Math.Sin(ang) * dd;
                pt.z = Math.Cos(ang) * dd;
                pt.y += pt.vy * tt * ((Math.Sqrt(distributionRadius) - dd) * 2);
                if (pt.y > vortexHeight / 2.0 || dd < 0.25)
                {
                    points.RemoveAt(i);
                    SpawnParticle();
                }
            }
        }

        double Elevation(double x, double y, double z)
        {
            double dist = Math.Sqrt(x * x + y * y + z * z);
            if (dist > 0 && z / dist >= -1 && z / dist <= 1) return Math.Acos(z / dist);
            return 1e-8;
        }

        // 把当前粒子分桶并加入 trailQueue（轮替保存最近 trailFrames 帧）
        void CommitTrail()
        {
            // 清理 oldest frame 并 push 新的 frame
            if (trailQueue.Count >= trailFrames) trailQueue.Dequeue();

            var buckets = new Point3DCollection[BUCKETS];
            for (int b = 0; b < BUCKETS; b++) buckets[b] = new Point3DCollection();

            // 先计算每个粒子的投影距离用于排序和桶分配
            foreach (var pt in points)
            {
                // 计算 camera space 投影逻辑：复用原 JS 的变换（简化，不取严格屏幕点）
                double x = pt.x - camX;
                double y = pt.y - (camY - 8);
                double z = pt.z - camZ;
                double p = Math.Atan2(x, z);
                double d = Math.Sqrt(x * x + z * z);
                x = Math.Sin(p - yaw) * d;
                z = Math.Cos(p - yaw) * d;
                p = Math.Atan2(y, z);
                d = Math.Sqrt(y * y + z * z);
                y = Math.Sin(p - pitch) * d;
                z = Math.Cos(p - pitch) * d;
                double dist = x * x + y * y + z * z;
                pt.dist = dist;

                // 根据 pt.colorVal 决定 bucket（6 桶）
                double col = pt.colorVal; // 随半径和帧号变化
                int bucket = (int)((col % 1.0) * BUCKETS);
                if (bucket < 0) bucket = 0;
                if (bucket >= BUCKETS) bucket = BUCKETS - 1;

                // 插入到对应 bucket 的 Point3DCollection 中
                // 使用投影后的 x,y,z（但 Helix PointsVisual3D 期望世界坐标）
                // 我们保留世界坐标（pt.x,pt.y,pt.z），Helix 将根据摄像机投影显示
                buckets[bucket].Add(new Point3D(pt.x, pt.y, pt.z));
            }

            trailQueue.Enqueue(buckets);
        }

        // 每帧把 trailQueue 合并到每个 Bucket 的 PointsVisual3D 上（老帧透明度更低）
        void RenderBuckets()
        {
            // 清空集合，按照 trailQueue 的顺序把最近帧放在最后（较亮）
            var merged = new Point3DCollection[BUCKETS];
            for (int b = 0; b < BUCKETS; b++) merged[b] = new Point3DCollection();

            foreach (var frameBuckets in trailQueue)
            {
                for (int b = 0; b < BUCKETS; b++)
                {
                    // append (更旧的帧先添加，显示上会被后面的覆盖，从而产生拖影)
                    foreach (var pt in frameBuckets[b])
                        merged[b].Add(pt);
                }
            }

            // 更新 UI：每个 bucket 对应一个 PointsVisual3D（在 XAML 按序命名）
            ParticlesBucket0.Points = merged[0];
            ParticlesBucket1.Points = merged[1];
            ParticlesBucket2.Points = merged[2];
            ParticlesBucket3.Points = merged[3];
            ParticlesBucket4.Points = merged[4];
            ParticlesBucket5.Points = merged[5];

            // 可选：根据距离调整 particle Size（近大远小）——PointsVisual3D 的 Size 是全局的，所以略难做细粒度
            // 这里不变动 Size，如需更真实效果可将近粒子单独分离为不同 PointsVisual3D 并设置更大 Size。
        }
    }
}
